package plugins;

import java.util.Vector;

import plugins.evento.DPluginRegisterEvent;

import Deventos.DEvent;
import Deventos.enlaceJS.DConector;

import componentes.base.DComponenteBase;

/**
 * Clase encargada de la gestión de los plugins, controlar el acceso a los
 * plugins y notificar a las hebras que asi lo deseen los cambios producidos en
 * los plugins
 * 
 * @author Ana Belen Pelegrina Ortiz. Carlos Rodriguez Dominguez
 */
public class PluginContainer extends DComponenteBase
{
	private static final long serialVersionUID = -7505153623522584387L;

	/**
	 * Vector de plugins
	 */
	private static Vector<DAbstractPlugin> plugins = null;

	/**
	 * Monitor encargado de sincronizar el acceso a los plugins
	 */
	private static MonitorPlugins monitor = null;

	/**
	 * Flag booleano que indica si el contenedor ha sido ya creado
	 */
	private static boolean created = false;
	
	/**
	 * 
	 */
	private static PluginContainer esto = null;
	
	/**
	 * Inicializa el contenedor de plugins si no ha sido ya inicializado
	 */
	public PluginContainer()
	{
		
		// nos conectamos directamente con el DConector
		super("PluginContainer", true, null);
			
		if (!created)
		{

			// cargar los plugins
			try
			{
				esto = this;
				plugins = DPluginLoader.getAllPlugins("plugin");
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}

			monitor = new MonitorPlugins();
			created = true;
		}
	}
	
	public static PluginContainer getPC() {
		return esto;
	}

	@Override
	public void procesarEvento(DEvent evento)
	{
		
		if (DConector.Dusuario.equals(evento.usuario)) return;
		
		if (evento.tipo.intValue() == DPluginRegisterEvent.SINCRONIZACION.intValue()) {
			DPluginRegisterEvent evt = (DPluginRegisterEvent) evento;
			
			
			
			for(DAbstractPlugin p: plugins) {
				
				if (p.nombre.equals(evt.nombre)) {
					p.procesarEvento(evt);
				}
			}
		}
	}

	@Override
	public synchronized void procesarEventoHijo(DEvent evento)
	{
		
		this.enviarEvento(evento);
	}

	/**
	 * La hebra que ejecuta este metodo espera hasta que se notifique algun
	 * cambio en la lista de plugins
	 */
	public static void actualizar()
	{
		try{
			monitor.actualizar();
		}catch(NullPointerException ex)
		{
			
		}
	}

	/**
	 * Permite acceder al plugin i-esimo
	 * 
	 * @param index
	 *            Posicion del plugin en la lista
	 * @return El plugin si el valor del indice es correcto, -1 en caso
	 *         contrario
	 */
	public static DAbstractPlugin getPlugin(int index)
	{
		if (index >= 0 && index < plugins.size())
			return plugins.get(index);
		else return null;
	}

	/**
	 * Elimina un plugin de la lista de plugins
	 * 
	 * @param namen
	 *            Nombre del plugin a eliminar
	 */
	public static void eliminarPlugin(String namen)
	{

		boolean encontrada = false;

		for (int i = 0; i < plugins.size() && !encontrada; ++i)
			if (plugins.get(i).getName().equals(namen))
			{
				encontrada = true;
				plugins.remove(i);
			}

		// notificamos la eliminacion del plugin
		monitor.notificarPlugins();
	}

	/**
	 * Comprueba si el i-esimo plugin de la lista es visible
	 * 
	 * @param i
	 *            Posicion del plugin
	 * @return True si el plugin es visible. False en caso contrario
	 */
	public static boolean isVisible(int i)
	{
		return plugins.get(i).shouldShowIt();
	}

	/**
	 * Establece si un plugin ha de ser visible o no
	 * 
	 * @param b
	 *            Indica si el plugin ha de ser visible de forma predeterminada
	 *            o no
	 * @param name
	 *            Nombre del plugin
	 */
	public static void setVisible(boolean b, String name)
	{

		boolean encontrada = false;

		for (int i = 0; i < plugins.size() && !encontrada; ++i)
			if (plugins.get(i).getName().equals(name))
			{
				encontrada = true;
				plugins.get(i).setShouldShowit(b);
			}

		// notificamos la eliminacion del plugin
		monitor.notificarPlugins();
	}

	/**
	 * Agrega un plugin a la lista
	 * 
	 * @param a
	 *            Plugin a agregar
	 */
	public static void agregarPlugin(DAbstractPlugin a)
	{
		plugins.add(a);

		// notificamos la insercion de un nuevo plugin
		monitor.notificarPlugins();
	}

	/**
	 * Consulta el numero de plugins cargados actualmente
	 * 
	 * @return Numero de plugins cargados actualmente. Devuelve -1 si se ha
	 *         producido algun error.
	 */
	public static int numPlugins()
	{

		if (plugins == null)
			return -1;
		else return plugins.size();
	}

	/**
	 * Consulta el nombre del fichero jar asociado a un plugin
	 * 
	 * @param index
	 *            Posicion del plugin en la lista
	 * @return Nombre del jar que contiene el plugin
	 * @throws ArrayIndexOutOfBoundsException
	 */
	public static String getPluginJarName(int index)
			throws ArrayIndexOutOfBoundsException
	{
		String jarName = plugins.get(index).getJarFile();

		return jarName;
	}

	/**
	 * Consulta la version del plugin
	 * 
	 * @param index
	 *            Posicion del plugin en la lista
	 * @return Version del plugin
	 * @throws ArrayIndexOutOfBoundsException
	 */
	public static long getVersionPlugin(int index)
			throws ArrayIndexOutOfBoundsException
	{
		long v = plugins.get(index).getVersion();

		return v;
	}

	/**
	 * Consulta el nombre de un plugin
	 * 
	 * @param index
	 *            Posicion del plugin en la lista
	 * @return Nombre del plugin
	 * @throws ArrayIndexOutOfBoundsException
	 */
	public static String getPluginName(int index)
			throws ArrayIndexOutOfBoundsException
	{
		String jarName = plugins.get(index).getName();

		return jarName;
	}

	/**
	 * Monitor que controla la actualizacion de la lista de aplicaciones
	 */
	private class MonitorPlugins
	{
		/**
		 * Metodo que controla el acceso a la lista de plugins. Toda hebra que
		 * llame a este metodo se queda "dormida" hasta que se ejecute el metodo
		 * notificarPlugins()
		 */
		public synchronized void actualizar()
		{
			try
			{
				wait();
			}
			catch (Exception e)
			{
				e.printStackTrace();
			}

		}

		/**
		 * Notifica a todas las hebras que estan esperando por una actulizacion
		 * de la lista de plugin que esta se ha producido. EL efecto de esta
		 * llamada es que se despiertan todas la hebras que estaban esperando;
		 * el planificador decide que hebra de la que se acaban de despertar se
		 * ejecutara primero.
		 */
		public synchronized void notificarPlugins()
		{
			notifyAll();
		}
	}
}
